`timescale 1ns / 1ps

`include "bus.v"
`include "opcode.v"
`include "funct.v"

module BranchGen(
  input       [`ADDR_BUS]     addr,
  input       [`INST_BUS]     inst,
  input       [`INST_OP_BUS]  op,
  input       [`FUNCT_BUS]    funct,
  input       [`DATA_BUS]     reg_data_1,
  input       [`DATA_BUS]     reg_data_2,
  output  reg                 branch_flag,
  output  reg [`ADDR_BUS]     branch_addr,
  output  reg                 next_inst_delayslot_flag              
);

  wire[`ADDR_BUS] addr_plus_4 = addr + 4;
  wire[25:0] jump_addr = inst[25:0];
  wire[`DATA_BUS] sign_ext_imm_sll2 = {{14{inst[15]}}, inst[15:0], 2'b00};
  //STAR btype ???000001??
  wire[4:0] btype= inst[20:16];

  always @(*) begin
    case (op)
      `OP_JAL,`OP_J: begin
        branch_flag <= 1;
        branch_addr <= {addr_plus_4[31:28], jump_addr, 2'b00};
        next_inst_delayslot_flag <= 1;
      end
      //STAR J
   
      `OP_SPECIAL: begin
        if (funct == `FUNCT_JALR || funct == `FUNCT_JR) begin
          branch_flag <= 1;
          branch_addr <= reg_data_1;
          next_inst_delayslot_flag <= 1;
        end
        else begin
          branch_flag <= 0;
          branch_addr <= 0;
          next_inst_delayslot_flag <= 0;
        end
      end

      // STAR BTYPE
      `OP_Btype: begin
          case (btype)
            // STAR BGEZ
            `OP_BGEZ: begin
              if (!reg_data_1[31]) begin
               branch_flag <= 1;
                branch_addr <= addr_plus_4 + sign_ext_imm_sll2;
              end
              else begin
                 branch_flag <= 0;
                  branch_addr <= 0;
              end
              next_inst_delayslot_flag <= 1;                
            end
            // STAR BGEZAL
            `OP_BGEZAL: begin
              if (!reg_data_1[31]) begin
               branch_flag <= 1;
                branch_addr <= addr_plus_4 + sign_ext_imm_sll2;
              end
              else begin
                 branch_flag <= 0;
                  branch_addr <= 0;
              end
              next_inst_delayslot_flag <= 1;   
            end
            // STAR BLTZ
            `OP_BLTZ: begin
              if (reg_data_1[31]) begin
               branch_flag <= 1;
                branch_addr <= addr_plus_4 + sign_ext_imm_sll2;
              end
              else begin
                 branch_flag <= 0;
                  branch_addr <= 0;
              end
              next_inst_delayslot_flag <= 1;                 
            end

            // STAR BLTZAL
            `OP_BLTZAL: begin
              if (reg_data_1[31]) begin
               branch_flag <= 1;
                branch_addr <= addr_plus_4 + sign_ext_imm_sll2;
              end
              else begin
                 branch_flag <= 0;
                  branch_addr <= 0;
              end
              next_inst_delayslot_flag <= 1;                 
            end
            default: begin 
                branch_flag <= 0;
                branch_addr <= 0;
                next_inst_delayslot_flag <= 0;
            end


          endcase
        end


      // STAR BGTZ
      `OP_BGTZ: begin
        if (!reg_data_1[31]&&reg_data_1) begin
          branch_flag <= 1;
          branch_addr <= addr_plus_4 + sign_ext_imm_sll2;
        end
        else begin
            branch_flag <= 0;
            branch_addr <= 0;
        end
        next_inst_delayslot_flag <= 1;  
      end

      // STAR BLEZ
      `OP_BLEZ: begin
        if (reg_data_1[31]||!reg_data_1) begin
          branch_flag <= 1;
          branch_addr <= addr_plus_4 + sign_ext_imm_sll2;
        end
        else begin
            branch_flag <= 0;
            branch_addr <= 0;
        end
        next_inst_delayslot_flag <= 1;        
      end

      `OP_BEQ: begin
        if (reg_data_1 == reg_data_2) begin
          branch_flag <= 1;
          branch_addr <= addr_plus_4 + sign_ext_imm_sll2;
        end
        else begin
          branch_flag <= 0;
          branch_addr <= 0;
        end
        next_inst_delayslot_flag <= 1;
      end

      `OP_BNE: begin
        if (reg_data_1 != reg_data_2) begin
          branch_flag <= 1;
          branch_addr <= addr_plus_4 + sign_ext_imm_sll2;
        end
        else begin
          branch_flag <= 0;
          branch_addr <= 0;
        end
        next_inst_delayslot_flag <= 1;
      end

    endcase
  end

endmodule // BranchGen
